use std::{env, fs}; 
use pest::{error::LineColLocation, Parser};
use pest_derive::Parser;

#[derive(Parser)]
#[grammar = "lexer.pest"]
pub struct LexerParser;

fn parse_number(text: &str) -> Option<i64> {
    if let Some(hex) = text.strip_prefix("0x") {
        i64::from_str_radix(hex, 16).ok()
    } else if let Some(oct) = text.strip_prefix("0") {
        i64::from_str_radix(oct, 8).ok()
    } else {
        text.parse::<i64>().ok()
    }
}

fn tokenize(input: &str) -> (Vec<String>, Vec<String>) {
    let mut normal_vec = vec![];
    let mut error_vec = vec![];
    match LexerParser::parse(Rule::FILE, input) {
        Ok(mut file) => {
            let token_pairs = file.next().unwrap().into_inner().next().unwrap();

            for token_pair in token_pairs.into_inner() {
                let rule = token_pair.as_rule();
                let span = token_pair.as_span();
                let text = span.as_str();
                let (col, _) = span.start_pos().line_col();

                if rule == Rule::ERROR {
                    error_vec.push(format!("Error type A at Line {}:unknown char {}.", col, text));
                    // eprintln!("Error type A at Line {}:unknown char {}.", col, text);
                }
                else {
                    if let Some(num) = parse_number(text) {
                        normal_vec.push(format!("{} {} at Line {}.", format!("{:?}", rule), num, col))
                        // eprintln!("{} {} at Line {}.", format!("{:?}", rule), num, col);
                    } else {
                        normal_vec.push(format!("{} {} at Line {}.", format!("{:?}", rule), text, col))
                        // eprintln!("{} {} at Line {}.", format!("{:?}", rule), text, col);
                    }
                }
            }
            (normal_vec, error_vec)
        },
        Err(e) => {
            if let LineColLocation::Pos((err_line, _)) = e.line_col {
                error_vec.push(format!("Error type A at Line {}: {}.", err_line, e.line()));
                // eprintln!("Error type A at Line {}: {}.", err_line, e.line());
            }
            (normal_vec, error_vec)
        }

    }
}

fn main() {
    let args: Vec<String> = env::args().collect();

    if args.len() < 2 {
        eprintln!("Usage: {} <filename>", args[0]);
        std::process::exit(1);
    }

    let filename = &args[1];

    let input = fs::read_to_string(filename).expect("Failed to read file");
    let (normal, err) = tokenize(&input);

    let str_vec = if err.is_empty() {&normal} else {&err};

    for ele in str_vec.into_iter() {
        eprintln!("{}", ele);
    }
}
